package com.cgy.utils.base.reflect;

import com.cgy.utils.base.strings.StringHelper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import javassist.*;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 反射工具类
 *
 * @author CHTK
 */
public class ReflectHelper {

	public static final String VOID = "void";
	public static final String STRING = "string";
	public static final String INT = "int";
	public static final String LONG = "long";
	public static final String DOUBLE = "double";
	public static final String FLOAT = "float";
	public static final String BYTE = "byte";
	public static final String CHAR = "char";
	public static final String SHORT = "short";
	public static final String BOOLEAN = "boolean";

	private static ConcurrentHashMap<String, ClassInfoContent> CLASS_INFO_CACHE = new ConcurrentHashMap<>();
	private static ConcurrentHashMap<String, Set<Object>> FIEDLS_CACHE = new ConcurrentHashMap<>();
	private static ConcurrentHashMap<String, Set<Object>> SUB_CLASS_CACHE = new ConcurrentHashMap<>();
	private static ConcurrentHashMap<String, Set<Object>> METHOD_CACHE = new ConcurrentHashMap<>();
	private static ConcurrentHashMap<String, Set<Object>> PARAMTERS_CACHE = new ConcurrentHashMap<>();

	/**
	 * 获取接口实现
	 *
	 * @param classes 类
	 * @return
	 */
	public static <T> Set<T> getSubClassTypes(final Class<T> classes) {
		Set<T> sets = Sets.newHashSet();
		Set<Class<? extends T>> subTypes = getSubTypes(classes);
		for (Class<? extends T> subType : subTypes) {
			try {
				T t = subType.newInstance();
				sets.add(t);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return sets;
	}

	/**
	 * 获取接口实现
	 *
	 * @param classes 类
	 * @return
	 */
	public static <T> Set<Class<? extends T>> getSubTypes(final Class<T> classes) {
		if (null != classes) {
			Package aPackage = classes.getPackage();
			String name = aPackage.getName();
			return getSubTypes(name, classes);
		}
		return null;
	}

	/**
	 * 获取接口实现
	 *
	 * @param classes 类
	 * @return
	 */
	public static <T> Set<T> getSubClassTypes(final String packageName, final Class<T> classes) {
		Set<T> sets = Sets.newHashSet();
		Set<Class<? extends T>> subTypes = getSubTypes(packageName, classes);
		for (Class<? extends T> subType : subTypes) {
			try {
				T t = subType.newInstance();
				sets.add(t);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return sets;
	}

	/**
	 * 获取接口实现
	 *
	 * @param packageName 包名
	 * @param classes     类
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> Set<Class<? extends T>> getSubTypes(final String packageName, final Class<T> classes) {
		Set<?> types = getTypes(packageName, classes, Type.SUB_TYPE);
		return (Set<Class<? extends T>>) types;
	}

	/**
	 * 渲染对象
	 * @param classes 对象
	 * @param params 参数
	 * @param <T>
	 * @return
	 */
	public static <T>T apply(Class<T> classes, Map<String, Object> params) {
		Preconditions.checkArgument(null != params || !params.isEmpty());
		CtClass ctClass = getCtClass(classes);
		if(null != ctClass) {
			String key;
			CtField declaredField = null;
			for (Map.Entry<String, Object> entry : params.entrySet()) {
				key = entry.getKey();
				if(StringUtils.isNotBlank(key)) {
					try {
						declaredField = ctClass.getDeclaredField(key);
					} catch (NotFoundException e) {
						e.printStackTrace();
					}
					if(null != declaredField && null != entry.getValue()) {
						declaredField.setAttribute(key, entry.getValue().toString().getBytes(StandardCharsets.UTF_8));
					}
				}
			}

			try {
				return (T) classes.newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	/**
	 * 解析类中所有相关信息
	 *
	 * @param packageName 包名
	 * @param classes     类
	 * @param type        查询类型
	 * @param annotation  注解
	 * @return
	 */
	public static Set<Object> getTypes(final String packageName, final Class<?> classes, final Type type,
			final Annotation... annotation) {
		Set<Object> sets = Sets.newHashSet();
		if (StringUtils.isNotBlank(packageName) && null != classes) {
			Reflections reflections = new Reflections(packageName);
			if (Type.SUB_TYPE == type) {
				sets = SUB_CLASS_CACHE.get(classes.getName());
				if (null == sets || sets.isEmpty()) {
					@SuppressWarnings("unchecked")
					Set<Class<?>> subTypesOf = reflections.getSubTypesOf((Class<Object>) classes);
					for (Class<?> aClass : subTypesOf) {
						sets.add(aClass);
					}
					SUB_CLASS_CACHE.put(classes.getName(), sets);
				}
				return sets;
			} else if (Type.FIELD_ANNOTATION == type && null != annotation && annotation.length > 0) {
				sets = FIEDLS_CACHE.get(classes.getName());
				if (null == sets || sets.isEmpty()) {
					Set<Field> fieldsAnnotatedWith = reflections.getFieldsAnnotatedWith(annotation[0]);
					for (Field field : fieldsAnnotatedWith) {
						sets.add(field);
					}
					FIEDLS_CACHE.put(classes.getName(), sets);
				}
				return sets;
			} else if (Type.METHOD_ANNOTATION == type && null != annotation && annotation.length > 0) {
				sets = METHOD_CACHE.get(classes.getName());
				if (null == sets || sets.isEmpty()) {
					Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(annotation[0]);
					for (Method method : methodsAnnotatedWith) {
						sets.add(method);
					}
					METHOD_CACHE.put(classes.getName(), sets);
				}
				return sets;
			} else if (Type.TYPE_ANNOTATION == type && null != annotation && annotation.length > 0) {
				Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(annotation[0]);
				for (Class<?> aClass : typesAnnotatedWith) {
					sets.add(aClass);
				}
				return sets;
			} else if (Type.PARAM_ANNOTATION == type && null != annotation && annotation.length > 0) {
				sets = PARAMTERS_CACHE.get(classes.getName());
				if (null == sets || sets.isEmpty()) {
					Set<Method> methodsWithAnyParamAnnotated = reflections
							.getMethodsWithAnyParamAnnotated(annotation[0]);
					for (Method method : methodsWithAnyParamAnnotated) {
						sets.add(method);
					}
					PARAMTERS_CACHE.put(classes.getName(), sets);
				}
				return sets;

			}
		}
		return null;
	}

	/**
	 * 获取类字段
	 *
	 * @param classes 类
	 * @return
	 */
	public static List<String> getFields(Class<?> classes) {
		List<String> result = Lists.newArrayList();
		if (null == classes) {

		}

		return result;
	}

	/**
	 * 添加字段
	 *
	 * @param obj
	 * @param fieldInfoContent
	 */
	public static void addField(final Object obj, final ReflectFieldHelper.FieldInfoContent fieldInfoContent) {
		String s = ReflectFieldHelper.makeField(fieldInfoContent);
		if (StringHelper.isBlank(s)) {
			return;
		}
		ClassInfoContent classInfoContent = ctClass(obj, false, false);
		CtClass ctClass = classInfoContent.getCtClass();
		try {
			ctClass.addField(CtField.make(s, ctClass));
		} catch (CannotCompileException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取类信息
	 *
	 * @param obj  对象
	 * @param deal 处理动作
	 * 
	 *             <pre>
	 *             对象 | 是否获取缓存 | 是否处理字段 | 是否处理方法
	 *             </pre>
	 * 
	 * @return
	 */
	public static ClassInfoContent ctClass(final Object obj, final Boolean... deal) {

		boolean dealCache = true;
		boolean dealField = true;
		boolean dealMethod = true;
		if (null != deal) {
			if (deal.length == 1) {
				dealCache = deal[0];
			} else if (deal.length == 2) {
				dealCache = deal[0];
				dealField = deal[1];
			} else if (deal.length > 2) {
				dealCache = deal[0];
				dealField = deal[1];
				dealMethod = deal[2];
			}
		}

		CtClass ctClass = getCtClass(obj);
		ClassInfoContent classInfoContent = null;

		Class<?> aClass = obj.getClass();
		String name = aClass.getName();

		if (dealCache) {
			classInfoContent = CLASS_INFO_CACHE.get(name);
		}

		if (null == classInfoContent) {
			classInfoContent = new ClassInfoContent();
			classInfoContent.setClassName(name);

			if (null != ctClass) {
				classInfoContent.setCtClass(ctClass);

				if (dealField) {
					CtField[] declaredFields = ctClass.getDeclaredFields();
					Map<String, ReflectFieldHelper.FieldInfoContent> fieldInfoContentMap = new HashMap<>();
					for (CtField declaredField : declaredFields) {
						ReflectFieldHelper.FieldInfoContent fieldInfoContent = ReflectFieldHelper
								.ctField(declaredField);
						fieldInfoContentMap.put(fieldInfoContent.getFieldName(), fieldInfoContent);
					}
					classInfoContent.setFields(fieldInfoContentMap);
				}

				if (dealMethod) {
					CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
					Map<String, ReflectMethodHelper.MethodInfoContent> methodInfoContentHashMap = new HashMap<>();
					for (CtMethod ctMethod : declaredMethods) {
						ReflectMethodHelper.MethodInfoContent methodInfoContent = ReflectMethodHelper
								.ctMethod(ctMethod);
						methodInfoContentHashMap.put(methodInfoContent.getMethodName(), methodInfoContent);
					}

					classInfoContent.setMethods(methodInfoContentHashMap);
				}

			}

		}
		CLASS_INFO_CACHE.put(name, classInfoContent);
		return classInfoContent;

	}

	/**
	 * 获取ctClass
	 *
	 * @param obj
	 */
	public static CtClass getCtClass(final Object obj) {
		if (null != obj) {
			Class<?> aClass = obj.getClass();
			return getCtClass(aClass);
		}
		return null;
	}

	/**
	 * 获取ctClass
	 *
	 * @param classes
	 */
	public static CtClass getCtClass(final Class<?> classes) {
		if (null != classes) {
			String name = classes.getName();
			ClassPool classPool = ClassPool.getDefault();
			try {
				return classPool.get(name);
			} catch (NotFoundException e) {}
		}
		return null;
	}

	@Getter
	@Setter
	public static class ClassInfoContent {
		private String className;
		protected CtClass ctClass;
		private Map<String, ReflectMethodHelper.MethodInfoContent> methods;
		private Map<String, ReflectFieldHelper.FieldInfoContent> fields;
		private Map<String, ReflectAnnotationHelper.AnnotationInfoContent> annotations;
	}

	public static enum Type {
		/**
		 * 获取所有接口实现类
		 */
		SUB_TYPE,
		/**
		 * 获取所有字段信息
		 */
		FIELD_ANNOTATION,
		/**
		 * 获取所有方法信息
		 */
		METHOD_ANNOTATION,
		/**
		 * 获取所有方法参数信息
		 */
		PARAM_ANNOTATION,
		/**
		 * 获取所有类信息
		 */
		TYPE_ANNOTATION;
	}
}
/*
 * 
 * // 初始化工具类 Reflections reflections = new Reflections(new
 * ConfigurationBuilder().forPackages(basePackages).addScanners(new
 * SubTypesScanner()).addScanners(new FieldAnnotationsScanner()));
 * 
 * // 获取某个包下类型注解对应的类 Set<Class<?>> typeClass =
 * reflections.getTypesAnnotatedWith(RpcInterface.class, true);
 * 
 * // 获取子类 Set<Class<? extends SomeType>> subTypes =
 * reflections.getSubTypesOf(SomeType.class);
 * 
 * // 获取注解对应的方法 Set<Method> resources
 * =reflections.getMethodsAnnotatedWith(SomeAnnotation.class);
 * 
 * // 获取注解对应的字段 Set<Field> ids =
 * reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
 * 
 * // 获取特定参数对应的方法 Set<Method> someMethods =
 * reflections.getMethodsMatchParams(long.class, int.class);
 * 
 * Set<Method> voidMethods = reflections.getMethodsReturn(void.class);
 * 
 * Set<Method> pathParamMethods
 * =reflections.getMethodsWithAnyParamAnnotated(PathParam.class);
 * 
 * // 获取资源文件 Set<String> properties =
 * reflections.getResources(Pattern.compile(".*\\.properties"))
 */
